package com.jctrip.hotel.source.service.hworld.impl;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jctrip.hotel.domain.dao.HtSourceInfoDao;
import com.jctrip.hotel.domain.entity.HtSourceInfo;
import com.jctrip.hotel.source.model.hworld.req.*;
import com.jctrip.hotel.source.model.hworld.resp.*;
import com.jctrip.hotel.source.service.HotelCommonTaskService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.jctrip.hotel.common.constant.RedisKeyConstant;
import com.jctrip.hotel.common.enums.OrderStatusEnum;
import com.jctrip.hotel.common.enums.SourceEnum;
import com.jctrip.hotel.common.util.JsonUtil;
import com.jctrip.hotel.common.util.RedisUtil;
import com.jctrip.hotel.source.client.HworldClient;
import com.jctrip.hotel.source.client.HworldTokenClient;
import com.jctrip.hotel.source.model.OrderDataResult;
import com.jctrip.hotel.source.model.PriceCheckParam;
import com.jctrip.hotel.source.model.wehotel.req.OrderData;
import com.jctrip.hotel.source.service.hworld.HworldService;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author henry
 * @Date 2023/5/29 21:49
 * @Description
 */
@Service
@Slf4j
public class HworldServiceImpl implements HworldService {

    @Resource
    private HworldClient hworldClient;
    @Resource
    private HworldTokenClient tokenClient;
    @Autowired
    private RedisUtil redisUtil;
    @Resource
    private HtSourceInfoDao htSourceInfoDao;
    @Autowired
    private HotelCommonTaskService hotelCommonTaskService;

    @Value("${feign.variables.hworld.clientId}")
    private String clientId;
    @Value("${feign.variables.hworld.secret}")
    private String secret;

    @Override
    public String parseHotelId(String innId) {
        if (StringUtils.isBlank(innId)) {
            return null;
        }
        if (innId.startsWith(SourceEnum.HWORLD.getCode())) {
            return innId.substring(SourceEnum.HWORLD.getCode().length());
        }
        return innId;
    }

    @Override
    public String parseInnId(String hotelId) {
        if (null == hotelId) {
            return null;
        }
        return SourceEnum.HWORLD.getCode() + hotelId;
    }

    @Override
    public HworldTokenResult token() throws UnsupportedEncodingException {
        String signature = StringUtils.join("Basic ",
                Base64.getEncoder().encodeToString(String.format("%s:%s", clientId,
                                secret)
                        .getBytes("utf-8")));

        return tokenClient.getToken(signature);
    }

    @Override
    public String tokenCache(String appId, String key) {
        try {
            String token = (String) redisUtil.get(RedisKeyConstant.HWORLD_TOKEN_CACHE);
            //  token 没有过期
            if (StringUtils.isNotBlank(token)) {
                return "Bearer " + token;
            }

            if (redisUtil.setENx(RedisKeyConstant.HWORLD_TOKEN_CACHE, 1 * 10,
                    RedisKeyConstant.HWORLD_TOKEN_CACHE)) {
                HworldTokenResult hworldTokenData = token();
                redisUtil.set(RedisKeyConstant.HWORLD_TOKEN_CACHE, hworldTokenData
                                .getAccess_token(),
                        hworldTokenData.getExpires_in() - 120);
                return "Bearer " + hworldTokenData.getAccess_token();
            } else {
                Thread.sleep(1 * 1000);
            }
            token = (String) redisUtil.get(RedisKeyConstant.HWORLD_TOKEN_CACHE);
            // 随眠1秒再次获取
            return "Bearer " + token;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException("获取access_token 失败");
        }
    }


    @Override
    public List<HworldHotelInfo> getHotelList(String appId, String key) {
        String token = tokenCache(appId, key);
        BaseReq req = new BaseReq();
        req.setCardId(appId);
        req.setCardPassWord(key);
        HworldResult<List<HworldHotelInfo>> hworldResult = hworldClient.getHotelList(token, req);
        if (null == hworldResult) {
            return null;
        }
        if (200 != hworldResult.getCode()) {
            throw new RuntimeException(hworldResult.getMessage());
        }
        return hworldResult.getContent();
    }

    @Override
    public List<HworldHotelDetail> getHotelComplexList(String appId, String key, String hotelId) {
        if (StringUtils.isBlank(hotelId)) {
            throw new RuntimeException("酒店ID为空");
        }
        String token = tokenCache(appId, key);
        HotelComplexListReq req = new HotelComplexListReq();
        req.setCardId(appId);
        req.setCardPassWord(key);
        req.setHotelIdList(Arrays.asList(hotelId));
        HworldResult<List<HworldHotelDetail>> hworldResult =
                hworldClient.getHotelComplexList(token, req);
        if (null == hworldResult) {
            return new ArrayList<>();
        }
        if (200 != hworldResult.getCode()) {
            throw new RuntimeException(hworldResult.getMessage());
        }
        return hworldResult.getContent();
    }

    @Override
    public List<HworldHoteRoomType> getHotelRoomType(String appId, String key, String hotelId) {
        if (StringUtils.isBlank(hotelId)) {
            throw new RuntimeException("酒店ID为空");
        }
        String token = tokenCache(appId, key);
        HotelComplexListReq req = new HotelComplexListReq();
        req.setHotelIdList(Arrays.asList(hotelId));
        req.setCardId(appId);
        req.setCardPassWord(key);
        HworldResult<List<HworldHotelDetail>> hworldResult =
                hworldClient.getHotelComplexList(token, req);
        if (null == hworldResult) {
            return new ArrayList<>();
        }
        if (200 != hworldResult.getCode()) {
            throw new RuntimeException(hworldResult.getMessage());
        }
        //一次只取一个
        HworldHotelDetail hworldHotelDetail = hworldResult.getContent().get(0);
        return hworldHotelDetail.getHotelRoomTypeList();
    }

    /**
     * 获取会员价(协议价)房态信息
     */
    @Override
    public HworldRoomDTO getTMCRoomPrice(String appId, String key, String hotelId, String start, String end,
            String roomTypeCode, String memberLevel) {
        try {
            String token = tokenCache(appId, key);
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            Date checkInDate = formatter.parse(start);
            Date checkOutDate = formatter.parse(end);

            HworldTMCRoomPriceReq req =
                    HworldTMCRoomPriceReq.builder().checkInDate(checkInDate)
                            .checkOutDate(checkOutDate).hotelIdList(Arrays.asList(hotelId))
                            .memberLevelList(Arrays.asList(memberLevel)).build();
            req.setCardId(appId);
            req.setCardPassWord(key);
            if (StringUtils.isNotBlank(roomTypeCode)) {
                req.setRoomTypeList(Arrays.asList(roomTypeCode));
            }

            HworldResult<List<TMCRoomPriceResult>> result = hworldClient.getTMCRoomPrice(token, req);
            if (null == result) {
                return null;
            }
            if (200 != result.getCode()) {
                throw new Exception(result.getMessage());
            }

            // ToDo   具体取哪一个待确定
            TMCRoomPriceResult tmcRoomPriceResult = result.getContent().get(0);
            return tmcRoomPriceResult.getRoomRateList().stream()
                    .filter(roomDTO -> roomDTO.getTagList().contains("Base"))
                    .findFirst().orElse(null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取华住TMC所有房态信息
     */
    public List<HworldRoomDTO> getTMCAllRoomPrice(String appId, String key, String hotelId, String start, String end,
                                                  String roomTypeCode, String memberLevel) {
        try {
            String token = tokenCache(appId, key);
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            Date checkInDate = formatter.parse(start);
            Date checkOutDate = formatter.parse(end);

            HworldTMCRoomPriceReq req =
                    HworldTMCRoomPriceReq.builder().checkInDate(checkInDate)
                            .checkOutDate(checkOutDate).hotelIdList(Arrays.asList(hotelId))
                            .memberLevelList(Arrays.asList(memberLevel)).build();
            req.setCardId(appId);
            req.setCardPassWord(key);
            if (StringUtils.isNotBlank(roomTypeCode)) {
                req.setRoomTypeList(Arrays.asList(roomTypeCode));
            }

            HworldResult<List<TMCRoomPriceResult>> result = hworldClient.getTMCRoomPrice(token, req);
            if (null == result) {
                return null;
            }
            if (200 != result.getCode()) {
                throw new Exception(result.getMessage());
            }
            TMCRoomPriceResult tmcRoomPriceResult = result.getContent().get(0);
            return tmcRoomPriceResult.getRoomRateList();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public HworldCheckAvailabilityDTO checkAvailability(String appId, String key, HworldCheckAvailabilityReq req) {
        req.setCardId(appId);
        req.setCardPassWord(key);
        req.setIsGroup(true);
        if (StringUtils.isBlank(req.getGuaranteeMode())) {
            req.setGuaranteeMode("FIRST_NIGHT");
        }
        String token = tokenCache(appId, key);
        HworldResult<HworldCheckAvailabilityDTO> result =
                hworldClient.checkAvailability(token, req);
        if (null == result) {
            return null;
        }
        if (200 != result.getCode()) {
            throw new RuntimeException(result.getMessage());
        }
        if (result.getContent().getRoomCount().getAvailableCount() == 0) {
            throw new RuntimeException("房量不足");
        }
        return result.getContent();
    }

    @Override
    public String addOrder(String appId, String key, HworldAddOrderReq req) {
        if (StringUtils.isBlank(appId) || StringUtils.isBlank(key) || req == null) {
            throw new RuntimeException("参数为空");
        }
        if (StringUtils.isBlank(req.getRequestNo())) {
            throw new RuntimeException("订单编号为空");
        }
        if (req.getAdults() == 0) {
            throw new RuntimeException("入住人数为空");
        }
        if (StringUtils.isBlank(req.getLinkName())) {
            throw new RuntimeException("联系人姓名为空");
        }
        if (StringUtils.isBlank(req.getLinkMobile())) {
            throw new RuntimeException("联系人手机为空");
        }
        if (StringUtils.isBlank(req.getHotelId())) {
            throw new RuntimeException("酒店id为空");
        }
        if (StringUtils.isBlank(req.getRoomTypeId())) {
            throw new RuntimeException("房型id为空");
        }
        if (req.getCheckInDate() == null) {
            throw new RuntimeException("入住时间为空");
        }
        if (req.getCheckOutDate() == null) {
            throw new RuntimeException("离店时间为空");
        }
        if (req.getRoomCount() == 0) {
            throw new RuntimeException("房间数量为空");
        }
        if (CollectionUtils.isEmpty(req.getDailyPriceList())) {
            throw new RuntimeException("每日房价列表为空");
        }
        if (StringUtils.isBlank(req.getRateCode())) {
            throw new RuntimeException("房价码为空");
        }
        req.setCardId(appId);
        req.setCardPassWord(key);
        req.setIsGroup(true);
//        req.setGuaranteeMode("FIRST_NIGHT");
        req.setApiType("INTERFACE");
        req.setIsNeedRegisterMember(false);
        req.setCheckInType("DAY");
        String token = tokenCache(appId, key);
        HworldResult<HworldOrderInfo> result = hworldClient.addOrder(token, req);
        hotelCommonTaskService.insertOrderErrorLog(req.getRequestNo(), JsonUtil.toString(req), JsonUtil.toString(result));
        if (null == result) {
            hotelCommonTaskService.insertOrderErrorLog(req.getRequestNo(), JsonUtil.toString(req), null);
            return null;
        }
        if (500 == result.getCode()) {
            // 查询一次
            try {
                Thread.sleep(100L);
            } catch (Exception e) {
                log.error("addOrder to query order error", e);
            }
            OrderData orderData = this.queryOrderData(appId, key, req.getRequestNo());
            return orderData.getOrderCode();
        }

        if (200 != result.getCode()) {
            hotelCommonTaskService.insertOrderErrorLog(req.getRequestNo(), JsonUtil.toString(req), result.getMessage());
            throw new RuntimeException(result.getMessage());
        }
        if (result.getContent().getBookStatus().equals("X")) {
            hotelCommonTaskService.insertOrderErrorLog(req.getRequestNo(), JsonUtil.toString(req), "下单失败");
            throw new RuntimeException("下单失败");
        }
        return result.getContent().getOrderId();
    }

    @Override
    public OrderDataResult postOrder(String appId, String key, HworldAddOrderReq req, PriceCheckParam priceCheck) {
        // 下单前校验，顺带获取每日房价作为参数进行下单
        HworldCheckAvailabilityReq checkAvailabilityReq =
                HworldCheckAvailabilityReq.builder().hotelId(req.getHotelId()).roomTypeId(req.getRoomTypeId())
                        .checkInDate(req.getCheckInDate()).checkOutDate(req.getCheckOutDate())
                        .rateCode(req.getRateCode()).bookerId(req.getBookerId()).build();
        HworldCheckAvailabilityDTO hworldCheckAvailabilityDTO = checkAvailability(appId, key, checkAvailabilityReq);
        List<HworldPriceDailyDTO> priceDailyList = hworldCheckAvailabilityDTO.getPriceDailyList();
        if (CollectionUtils.isEmpty(priceDailyList)) {
            hotelCommonTaskService.insertOrderErrorLog(req.getRequestNo(), JsonUtil.toString(req), "每日房价异常");
            throw new RuntimeException("每日房价异常");
        }

        List<HworldDailyPriceParameter> dailyPriceList = new ArrayList<>();
        AtomicReference<BigDecimal> hworldtotalRate = new AtomicReference<>(BigDecimal.ZERO);
        AtomicReference<BigDecimal> raisedTotalPrice = new AtomicReference<>(BigDecimal.ZERO);
        priceDailyList.forEach(priceDailyDTO -> {
            HworldDailyPriceParameter dailyPriceParameter =
                    HworldDailyPriceParameter.builder().bizDate(priceDailyDTO.getBizDate())
                            .afterTaxPrice(priceDailyDTO.getAfterTaxPrice()).build();
            dailyPriceList.add(dailyPriceParameter);
            hworldtotalRate.set(hworldtotalRate.get().add(priceDailyDTO.getCompanyToGroupPrice()));
            raisedTotalPrice.set(raisedTotalPrice.get()
                    .add(priceCheck.getRaiseFunc().apply(priceDailyDTO.getCompanyToGroupPrice())));
        });
        // todo 临时去掉价格还原
//        if (priceCheck.getTotalRate().compareTo(raisedTotalPrice.get().multiply(BigDecimal.valueOf(req.getRoomCount()))
//                .subtract(priceCheck.getTolerancePrice())) < 0) {
//            log.error("订单销售价不能低于采购总价，订单销售价={}，采购总价={}, 容差金额={}",
//                    priceCheck.getTotalRate(), raisedTotalPrice, priceCheck.getTolerancePrice());
//            throw new RuntimeException("订单销售价不能低于采购总价");
//        }
        if (priceCheck.getTotalRate().compareTo(hworldtotalRate.get().multiply(BigDecimal.valueOf(req.getRoomCount()))
                .subtract(priceCheck.getTolerancePrice())) < 0) {
            String error = String.format("订单销售价不能低于采购总价，订单销售价=%s，采购总价=%s, 容差金额=%s, 目前差额=%s",
                    priceCheck.getTotalRate(),
                    hworldtotalRate.get().multiply(BigDecimal.valueOf(req.getRoomCount())),
                    priceCheck.getTolerancePrice(),
                    hworldtotalRate.get().multiply(BigDecimal.valueOf(req.getRoomCount())).subtract(priceCheck.getTotalRate()));
            hotelCommonTaskService.insertOrderErrorLog(req.getRequestNo(), JsonUtil.toString(req), error);
            log.error("订单销售价不能低于采购总价，订单销售价={}，采购总价={}, 容差金额={}, 目前差额={}",
                    priceCheck.getTotalRate(), hworldtotalRate.get().multiply(BigDecimal.valueOf(req.getRoomCount())),
                            priceCheck.getTolerancePrice(), hworldtotalRate.get().multiply(BigDecimal.valueOf(req.getRoomCount())).subtract(priceCheck.getTotalRate()));
            throw new RuntimeException("订单销售价不能低于采购总价");
        }

        req.setDailyPriceList(dailyPriceList);
        String outTradeNo = addOrder(appId, key, req);
        // build result
        OrderData orderData = new OrderData();
        orderData.setOrderCode(outTradeNo);
        // 0：待确认
        orderData.setOrderState(1);
        return OrderDataResult.builder().orderState(OrderStatusEnum.BOOK_SUCCESS.getCode()).orderCode(outTradeNo)
                .externalId(req.getRequestNo()).totalRate(hworldtotalRate.get().multiply(BigDecimal.valueOf(req.getRoomCount())))
                .requestJsonStr(JsonUtil.toString(req))
                .responseJsonStr(JsonUtil.toString(orderData)).build();
    }

    @Override
    public HworldOrderInfo queryOrder(String appId, String key, String orderId) {
        if (StringUtils.isBlank(orderId)) {
            throw new RuntimeException("订单编号为空");
        }
        String token = tokenCache(appId, key);
        QueryOrderReq req = new QueryOrderReq();
        req.setRequestNo(orderId);
        req.setCardId(appId);
        req.setCardPassWord(key);
        HworldResult<HworldOrderInfo> result =
                hworldClient.queryOrderByRequestNo(token, req);
        if (null == result) {
            return null;
        }
        if (200 != result.getCode()) {
            throw new RuntimeException(result.getMessage());
        }
        return result.getContent();
    }

    @Override
    public OrderData queryOrderData(String appId, String key, String orderId) {
        HworldOrderInfo hworldOrderInfo = queryOrder(appId, key, orderId);
        return parseOrderData(hworldOrderInfo);
    }

    @Override
    public Boolean cancelOrder(String appId, String key, HworldCancelOrderReq req) {
        String token = tokenCache(appId, key);
        CancelOrderReq cancelOrderReq = new CancelOrderReq();
        cancelOrderReq.setHotelId(req.getHotelId());
        cancelOrderReq.setOrderId(req.getOutTradeId());
        cancelOrderReq.setCardId(appId);
        cancelOrderReq.setCardPassWord(key);
        HworldResult<Boolean> result =
                hworldClient.cancelOrder(token, cancelOrderReq);
        if (null == result) {
            return null;
        }
        if (200 != result.getCode()) {
            throw new RuntimeException(result.getMessage());
        }
        return result.getContent();
    }

    @Override
    public HworldOrderInfo queryByRequestNo(String sourceId, String orderId) {
        if (StringUtils.isBlank(orderId)) {
            throw new RuntimeException("订单号为空");
        }
        HtSourceInfo htSourceInfo =
                htSourceInfoDao.selectOne(new LambdaQueryWrapper<HtSourceInfo>().eq(HtSourceInfo::getSourceId,
                        sourceId));
        if (htSourceInfo == null) {
            throw new RuntimeException("供应商账户为空");
        }
        String appId = htSourceInfo.getSourceAppId();
        String key = htSourceInfo.getSourceSecret();

        return queryOrder(appId, key, orderId);
    }

    @Override
    public List<HworldActivityRuleListResult> getActivityRuleList(String appId, String key) {
        try {
            String token = tokenCache(appId, key);
            BaseReq baseReq = new BaseReq();
            baseReq.setCardId(appId);
            baseReq.setCardPassWord(key);
            HworldResult<List<HworldActivityRuleListResult>> result = hworldClient.getActivityRuleList(token, baseReq);
            if (null == result) {
                return null;
            }
            if (200 != result.getCode()) {
                throw new Exception(result.getMessage());
            }
            return result.getContent();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<HworldCardInfo> getCardInfo(String appId, String key, List<String> bookerIds) {
        String token = tokenCache(appId, key);
        HworldGetCardInfoReq req = new HworldGetCardInfoReq();
        req.setCardId(appId);
        req.setCardPassWord(key);
        req.setBookerIds(bookerIds);
        HworldResult<List<HworldCardInfo>> hworldResult = hworldClient.getCardInfo(token,req);
        if (null == hworldResult){
            return null;
        }
        if (200 != hworldResult.getCode()) {
            throw new RuntimeException(hworldResult.getMessage());
        }
        return hworldResult.getContent();
    }


    private OrderData parseOrderData(HworldOrderInfo orderInfo) {
        if (null == orderInfo) {
            return null;
        }

        OrderData orderData = new OrderData();
        orderData.setOrderCode(orderInfo.getOrderId());
        orderData.setExternalId(orderInfo.getRequestNo());
        //          1-预定     2-取消   3-夜审取消（即noshow） 4-入住   5-离店
        Integer orderState = null;
        try {
            switch (orderInfo.getPmsAdapterOrderStatus()) {
                case "R":
                    orderState = 1;
                    break;
                case "X":
                    orderState = 2;
                    break;
                case "N":
                    orderState = 3;
                    break;
                case "I":
                    orderState = 4;
                    break;
                case "O":
                    orderState = 5;
                    break;
                default:
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        orderData.setOrderState(orderState);

        Integer payState = null;
        switch (orderInfo.getPayStatus() != null ? orderInfo.getPayStatus() : "NONE") {
            case "NONE":
                payState = 0;
                break;
            case "PAY":
                payState = 1;
                break;
            case "REFUND":
            default:
                payState = 3;
                break;

        }
        orderData.setPayState(payState);
        return orderData;
    }

}
